#include <qlib/geo/rectangle_img.h>
#include <cassert>
#include <cmath>
#include <limits>
#include <vector>

namespace qlib {

RectangleImg::RectangleImg() {
  width = 0;
  height = 0;
  layer = 0;
  datatype = 0;
  rotation = 0;
}

RectangleImg::RectangleImg(const Vec2d& _pos, double _width, double _height, uint32_t _layer, uint32_t _datatype) {
  pos = _pos;
  width = _width;
  height = _height;
  layer = _layer;
  datatype = _datatype;
  rotation = 0;
}
RectangleImg::RectangleImg(const Vec2d& _left_bottom, const Vec2d& _right_top, uint32_t _layer, uint32_t _datatype) {
  pos = _left_bottom;
  width = _right_top.x - _left_bottom.x;
  height = _right_top.y - _left_bottom.y;
  layer = _layer;
  datatype = _datatype;
  rotation = 0;
}

RectangleImg::RectangleImg(const RectangleImg& other) {
  operator=(other);
}
RectangleImg& RectangleImg::operator=(const RectangleImg& other) {
  layer = other.layer;
  datatype = other.datatype;
  pos = other.pos;
  rotation = other.rotation;
  width = other.width;
  height = other.height;
  repetition = other.repetition;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    properties[i] = Property::create();
    if(other.properties[i])
    {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  m_img_data = other.m_img_data;
  m_name = other.m_name;
  m_order_index = other.m_order_index;
  m_cell_points = other.m_cell_points;
  m_img_points = other.m_img_points;

  return *this;
}

RectangleImg::s_ptr RectangleImg::copy() const {
  auto res = RectangleImg::create();
  *res = *this;

  return res;
}

void RectangleImg::translate(double dx, double dy) {
  pos += {dx, dy};
}

void RectangleImg::init(const Vec2d& _pos, double _width, double _height, uint32_t _layer, uint32_t _datatype) {
  pos = _pos;
  width = _width;
  height = _height;
  layer = _layer;
  datatype = _datatype;
}
void RectangleImg::init(const Vec2d& _left_bottom, const Vec2d& _right_top, uint32_t _layer, uint32_t _datatype) {
  pos = _left_bottom;
  width = _right_top.x - _left_bottom.x;
  height = _right_top.y - _left_bottom.y;
  layer = _layer;
  datatype = _datatype;
}

Polygon::s_ptr RectangleImg::to_polygon() const {
  Polygon::s_ptr result = Polygon::create();
  result->layer = layer;
  result->datatype = datatype;

  result->points.resize(4);
  result->points[0] = pos;
  result->points[1] = pos + Vec2d(width, 0);
  result->points[2] = pos + Vec2d(width, height);
  result->points[3] = pos + Vec2d(0, height);

  auto center = pos + qlib::Vec2d(width / 2.0, height / 2.0);
  result->rotate(rotation, center);

  return result;
}

Vec2dArray RectangleImg::bounding_box() const {
  box.resize(2);
  if (rotation == 0) {
    box[0] = pos;
    box[1] = pos + Vec2d(width, height);
  } else {
    auto poly = to_polygon();
    box = poly->bounding_box();
  }
  return box;
}

qlib::RectangleImg getRectangleImg(qlib::Polygon::s_ptr polygon, double tolerance = 1e-6) {
  assert(polygon->points.size() == 4);

  // 计算中心点
  qlib::Vec2d center(0.0, 0.0);
  for (const auto& p : polygon->points) {
    center += p;
  }
  center /= 4.0;

  // 计算协方差矩阵的项
  double Sxx = 0.0, Syy = 0.0, Sxy = 0.0;
  for (const auto& p : polygon->points) {
    qlib::Vec2d rel = p - center;
    Sxx += rel.x * rel.x;
    Syy += rel.y * rel.y;
    Sxy += rel.x * rel.y;
  }
  Sxx /= 4.0;
  Syy /= 4.0;
  Sxy /= 4.0;

  // 计算主轴角度（旋转角度）
  double theta = 0.5 * std::atan2(2 * Sxy, Sxx - Syy);

  // 计算逆旋转后的坐标极值
  double min_x_rot = std::numeric_limits<double>::max();
  double max_x_rot = -std::numeric_limits<double>::max();
  double min_y_rot = std::numeric_limits<double>::max();
  double max_y_rot = -std::numeric_limits<double>::max();

  for (const auto& p : polygon->points) {
    qlib::Vec2d rel = p - center;
    // 应用逆旋转
    double x_rot = rel.x * std::cos(theta) + rel.y * std::sin(theta);
    double y_rot = -rel.x * std::sin(theta) + rel.y * std::cos(theta);

    if (x_rot < min_x_rot)
      min_x_rot = x_rot;
    if (x_rot > max_x_rot)
      max_x_rot = x_rot;
    if (y_rot < min_y_rot)
      min_y_rot = y_rot;
    if (y_rot > max_y_rot)
      max_y_rot = y_rot;
  }

  // 计算宽高，考虑容差
  double width = max_x_rot - min_x_rot;
  double height = max_y_rot - min_y_rot;

  // 计算左下角点
  qlib::Vec2d pos = center + qlib::Vec2d(min_x_rot, min_y_rot);

  // 返回恢复的矩形
  qlib::RectangleImg rect;
  rect.pos = pos;
  rect.rotation = theta;
  rect.width = width;
  rect.height = height;
  return rect;
}

void RectangleImg::mirror(qlib::Vec2d p0, qlib::Vec2d p1) {
  qlib::Polygon::s_ptr polygon = to_polygon();

  polygon->mirror(p0, p1);

  auto rect = getRectangleImg(polygon);
  pos = rect.pos;
  rotation = rect.rotation;
  width = rect.width;
  height = rect.height;

  return;
}

// void RectangleImg::mirror(qlib::Vec2d p0, qlib::Vec2d p1) {
//   Vec2d v = p1 - p0;
//   double tmp = v.length_sq();
//   if (tmp == 0)
//     return;

//   Vec2d r = v * (2 / tmp);
//   Vec2d p2 = p0 * 2;

//   Vec2d *p = &pos;
//   *p = v * (*p - p0).inner(r) - *p + p2;

//   Vec2d end = pos + qlib::Vec2d(width, height);
//   p = &end;

//   *p = v * (*p - p0).inner(r) - *p + p2;

//   end = end - pos;
//   width = end.x;
//   height = end.y;
// }

void RectangleImg::rotate(double angle, qlib::Vec2d center) {
  rotation += angle;
  center -= qlib::Vec2d(width / 2.0, height / 2.0);

  qlib::Vec2dArray points(1);
  Vec2d vt = pos - center;

  double ca = cos(angle);
  double sa = sin(angle);

  points[0].x = vt.x * ca - vt.y * sa + center.x;
  points[0].y = vt.x * sa + vt.y * ca + center.y;

  pos = points[0];
}

Vec2dArray RectangleImg::render_aabb() {
  return bounding_box();
}

Vec2dArray RectangleImg::render_poly() {
  auto p = to_polygon();
  return p->points;
}

Vec2dArray RectangleImg::render_absorp() {
  auto p = to_polygon();
  return p->points;
}

void RectangleImg::transform(double magnification, bool x_reflection, double _rotation, Vec2d origin) {
  rotation += _rotation;

  qlib::Vec2dArray points(2);
  points[0] = pos;
  points[1] = pos + qlib::Vec2d(width, height);

  double ca = cos(rotation);
  double sa = sin(rotation);

  Vec2d* p = points.data();
  for (uint64_t num = points.size(); num > 0; num--, p++) {
    Vec2 q = *p * magnification;
    if (x_reflection)
      q.y = -q.y;
    p->x = q.x * ca - q.y * sa + origin.x;
    p->y = q.x * sa + q.y * ca + origin.y;
  }

  pos = points[0];
  width = points[1].x - points[0].x;
  height = points[1].y - points[0].y;

  amend();
}

void RectangleImg::amend() {
  if (width < 0) {
    width = std::abs(width);
    pos.x -= width;
  }

  if (height < 0) {
    height = std::abs(height);
    pos.y -= height;
  }

  if (!(width > 0 && height > 0)) {
    width = 0.001;
    height = 0.001;
  }

  assert(width > 0 && height > 0);
}

uint32_t RectangleImg::getLayer() {
  return layer;
}
void RectangleImg::setLayer(uint32_t _layer) {
  layer = _layer;
}

double RectangleImg::getWidth() {
  return width;
}
void RectangleImg::setWidth(double _width) {
  pos.x -= (_width - width) / 2.0;
  width = _width;
}

double RectangleImg::getHeight() {
  return height;
}
void RectangleImg::setHeight(double _height) {
  pos.y -= (_height - height) / 2.0;
  height = _height;
}

Vec2d RectangleImg::getCenterPoint() {
  auto box = bounding_box();
  return (box[0] + box[1]) / 2.0;
}
void RectangleImg::setCenterPoint(Vec2d center) {
  auto box = bounding_box();
  auto center_pos = (box[0] + box[1]) / 2.0;

  auto trans = center - center_pos;
  translate(trans.x, trans.y);
}

double RectangleImg::getRotation() {
  return rotation;
}

void RectangleImg::setRotation(double _rotation) {
  rotation = _rotation;
}

double RectangleImg::area() const {
  return width * height;
}

double RectangleImg::perimeter() {
  return (width + height) * 2;
}
RectangleImgParts RectangleImg::get_parts() {
  auto poly = to_polygon();
  auto v_points = connect_end(poly->points);
  auto v_edges = get_edges(v_points);
  auto v_edges_mid = get_edges_mid(v_edges);

  auto box = bounding_box();
  auto box_points = box_to_points(box);
  auto box_edges = get_edges(box_points);
  auto box_edges_mid = get_edges_mid(box_edges);

  auto center = box_center(box);

  auto res = std::make_tuple(center, v_points, v_edges, v_edges_mid, box_points, box_edges, box_edges_mid);
  return res;
}

Vec2dArray RectangleImg::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  if (type == GeoPartType::Point)
    return Vec2dArray{std::get<1>(parts)[id]};
  if (type == GeoPartType::Edge)
    return std::get<2>(parts)[id];
  if (type == GeoPartType::EdgeMid)
    return Vec2dArray{std::get<3>(parts)[id]};
  if (type == GeoPartType::BoxPoint)
    return Vec2dArray{std::get<4>(parts)[id]};
  if (type == GeoPartType::BoxEdge)
    return std::get<5>(parts)[id];
  if (type == GeoPartType::BoxEdgeMid)
    return Vec2dArray{std::get<6>(parts)[id]};
  return Vec2dArray{};
}

}  // namespace qlib